/*
-----------------------------------------------------------------------------
This source file is part of WGE

Copyright  2008 WuYong

File Name:       
     wgeMath.h


Descritpion:
     The WGE math library's header file


***************************************************************************/
#ifndef _WGE_MATH_H_
#define _WGE_MATH_H_
#include "wgePrerequisites.h"

namespace wge{

	/**Standard 2-dimensional vector.
	   @remarks
	        none
	*/	
	class WGEEXPORT Vector2
	{
	public:
		///Constructor
		WGEINLINE Vector2(void)
		{

		}
		WGEINLINE Vector2(const float scaler):x(scaler), y(scaler)
		{

		}
		WGEINLINE Vector2(const float fx, const float fy):x(fx), y(fy)
		{

		}

		WGEINLINE Vector2(const float afCoordinate[2]):x(afCoordinate[0]), y(afCoordinate[1])
		{

		}

		///opeartor []
		WGEINLINE float operator[](const size_t i) const
		{
			assert(i<2);
			
			return *(&x+i);
		}

		WGEINLINE float& operator[](const size_t i)
		{
			assert(i<2);

			return *(&x+i);
		}

		///casting
		WGEINLINE operator float*()
		{
			return &x;
		}

		WGEINLINE  operator const float*() const
		{
			return &x;
		}

		///Assignment operator
		WGEINLINE Vector2& operator = (const Vector2& rkVector)
		{
			x = rkVector.x;
			y = rkVector.y;

			return *this;
		}

		WGEINLINE Vector2& operator  = (const float fScalar)
		{
			x = fScalar;
			y = fScalar;

			return *this;
		}

		WGEINLINE Vector2& operator += (const Vector2& rkVector)
		{
			x += rkVector.x;
			y += rkVector.y;

			return *this;
		}

		WGEINLINE Vector2& operator -=(const Vector2& rkVector)
		{
			x -= rkVector.x;
			y -= rkVector.y;
			
			return *this;
		}

		WGEINLINE Vector2& operator *= (const float fScalar)
		{
			x *= fScalar;
			y *= fScalar;

			return *this;
		}

		WGEINLINE Vector2& operator /= (const float fScalar)
		{
			assert(fScalar != 0.0);
			float fInv = 1.0 / fScalar;

			x *= fInv;
			y *= fInv;

			return *this;
		}

		
		WGEINLINE bool operator == (const Vector2& rkVector) const
		{
			return (x == rkVector.x  &&  y == rkVector.y);
		}

		WGEINLINE bool operator != (const Vector2& rkVector) const
		{
			return (x != rkVector.x || y != rkVector.y);
		}

		///Arithmetic operations
		WGEINLINE  Vector2 operator + (const Vector2& rkVector)const
		{
			return Vector2(x+rkVector.x, y+rkVector.y);
		}

		WGEINLINE Vector2 operator - (const Vector2& rkVector) const
		{
			return Vector2(x-rkVector.x, y-rkVector.y);
		}

		WGEINLINE Vector2 operator * (const float fscalar) const
		{
			return Vector2(x*fscalar, y*fscalar);
		}

	
		WGEINLINE Vector2 operator / (const float fScalar) const
		{
			assert(fScalar != 0.0);
			float fInv = 1.0/fScalar;

			return Vector2(x*fInv, y*fInv);

		}

		WGEINLINE const Vector2& operator + () const
		{
			return *this;
		}

		WGEINLINE Vector2 operator - () const
		{
			return Vector2(-x, -y);
		}


		///helper function
		WGEINLINE friend Vector2 operator * (const float fScalar, const Vector2& rkVector)
		{
			return Vector2(fScalar* rkVector.x, fScalar*rkVector.y);
		}
		
		/** Function for writing to a stream.
		*/
		inline WGEEXPORT friend std::ostream& operator <<
			( std::ostream& o, const Vector2& v )
		{
			o << "Vector2(" << v.x << ", " << v.y <<  ")";
			return o;
		}

		~Vector2(void){}


	public:
		float x, y;
	};


	/**Standard 3-dimensional vector.
	@remarks
	none
	*/	
	class WGEEXPORT Vector3
	{
	public:
		///Constructor
		WGEINLINE Vector3(void)
		{

		}
		WGEINLINE Vector3(const float scaler):x(scaler), y(scaler),z(scaler)
		{

		}
		WGEINLINE Vector3(const float fx, const float fy, const float fz):x(fx), y(fy), z(fz)
		{

		}

		WGEINLINE Vector3(const float afCoordinate[3]):x(afCoordinate[0]), y(afCoordinate[1]), z(afCoordinate[2])
		{

		}

		///opeartor []
		WGEINLINE float operator[](const size_t i) const
		{
			assert(i<3);

			return *(&x+i);
		}

		WGEINLINE float& operator[](const size_t i)
		{
			assert(i<3);

			return *(&x+i);
		}

		///casting
		WGEINLINE operator float*()
		{
			return &x;
		}

		WGEINLINE  operator const float*() const
		{
			return &x;
		}

		///Assignment operator
		WGEINLINE Vector3& operator = (const Vector3& rkVector)
		{
			x = rkVector.x;
			y = rkVector.y;
			z = rkVector.z;

			return *this;
		}

		WGEINLINE Vector3& operator  = (const float fScalar)
		{
			x = fScalar;
			y = fScalar;
			z = fScalar;

			return *this;
		}

		WGEINLINE Vector3& operator += (const Vector3& rkVector)
		{
			x += rkVector.x;
			y += rkVector.y;
			z += rkVector.z;

			return *this;
		}

		WGEINLINE Vector3& operator -=(const Vector3& rkVector)
		{
			x -= rkVector.x;
			y -= rkVector.y;
			z -= rkVector.z;

			return *this;
		}

		WGEINLINE Vector3& operator *= (const float fScalar)
		{
			x *= fScalar;
			y *= fScalar;
			z *= fScalar;

			return *this;
		}

		WGEINLINE Vector3& operator /= (const float fScalar)
		{
			assert(fScalar != 0.0);
			float fInv = 1.0 / fScalar;

			x *= fInv;
			y *= fInv;
			z *= fInv;

			return *this;
		}


		WGEINLINE bool operator == (const Vector3& rkVector) const
		{
			return (x == rkVector.x  &&  y == rkVector.y && z == rkVector.z);
		}

		WGEINLINE bool operator != (const Vector3& rkVector) const
		{
			return (x != rkVector.x || y != rkVector.y) || z != rkVector.z;
		}

		///Arithmetic operations
		WGEINLINE  Vector3 operator + (const Vector3& rkVector)const
		{
			return Vector3(x+rkVector.x, y+rkVector.y, z+rkVector.z);
		}

		WGEINLINE Vector3 operator - (const Vector3& rkVector) const
		{
			return Vector3(x-rkVector.x, y-rkVector.y, z-rkVector.z);
		}

		WGEINLINE Vector3 operator * (const float fscalar) const
		{
			return Vector3(x*fscalar, y*fscalar, z*fscalar);
		}


		WGEINLINE Vector3 operator / (const float fScalar) const
		{
			assert(fScalar != 0.0);
			float fInv = 1.0/fScalar;

			return Vector3(x*fInv, y*fInv, z*fInv);

		}

		WGEINLINE const Vector3& operator + () const
		{
			return *this;
		}

		WGEINLINE Vector3 operator - () const
		{
			return Vector3(-x, -y, -z);
		}


		///helper function
		WGEINLINE friend Vector3 operator * (const float fScalar, const Vector3& rkVector)
		{
			return Vector3(fScalar* rkVector.x, fScalar*rkVector.y, fScalar*rkVector.z);
		}




		///Destructor
		~Vector3(void){}


	public:
		float x, y, z;
	};


	/**Standard 4-dimensional vector.
	@remarks
	none
	*/	
	class WGEEXPORT Vector4
	{
	public:
		///Constructor
		WGEINLINE Vector4(void)
		{

		}
		WGEINLINE Vector4(const float scaler):x(scaler), y(scaler),z(scaler),w(scaler)
		{

		}
		WGEINLINE Vector4(const float fx, const float fy, const float fz, const float fw):x(fx), y(fy), z(fz),w(fw)
		{

		}

		WGEINLINE Vector4(const float afCoordinate[4]):x(afCoordinate[0]), y(afCoordinate[1]), z(afCoordinate[2]), w(afCoordinate[3])
		{

		}

		///opeartor []
		WGEINLINE float operator[](const size_t i) const
		{
			assert(i<4);

			return *(&x+i);
		}

		WGEINLINE float& operator[](const size_t i)
		{
			assert(i<4);

			return *(&x+i);
		}

		///casting
		WGEINLINE operator float*()
		{
			return &x;
		}

		WGEINLINE  operator const float*() const
		{
			return &x;
		}

		///Assignment operator
		WGEINLINE Vector4& operator = (const Vector4& rkVector)
		{
			x = rkVector.x;
			y = rkVector.y;
			z = rkVector.z;
			w = rkVector.w;

			return *this;
		}

		WGEINLINE Vector4& operator  = (const float fScalar)
		{
			x = fScalar;
			y = fScalar;
			z = fScalar;
			w = fScalar;

			return *this;
		}

		WGEINLINE Vector4& operator += (const Vector4& rkVector)
		{
			x += rkVector.x;
			y += rkVector.y;
			z += rkVector.z;
			w += rkVector.w;

			return *this;
		}

		WGEINLINE Vector4& operator -=(const Vector4& rkVector)
		{
			x -= rkVector.x;
			y -= rkVector.y;
			z -= rkVector.z;
			w -= rkVector.w;

			return *this;
		}

		WGEINLINE Vector4& operator *= (const float fScalar)
		{
			x *= fScalar;
			y *= fScalar;
			z *= fScalar;
			w *= fScalar;

			return *this;
		}

		WGEINLINE Vector4& operator /= (const float fScalar)
		{
			assert(fScalar != 0.0);
			float fInv = 1.0 / fScalar;

			x *= fInv;
			y *= fInv;
			z *= fInv;
			w *= fInv;

			return *this;
		}


		WGEINLINE bool operator == (const Vector4& rkVector) const
		{
			return (x == rkVector.x  &&  y == rkVector.y && z == rkVector.z && w == rkVector.w);
		}

		WGEINLINE bool operator != (const Vector4& rkVector) const
		{
			return (x != rkVector.x || y != rkVector.y) || z != rkVector.z || w != rkVector.w;
		}

		///Arithmetic operations
		WGEINLINE  Vector4 operator + (const Vector4& rkVector)const
		{
			return Vector4(x+rkVector.x, y+rkVector.y, z+rkVector.z, w+rkVector.w);
		}

		WGEINLINE Vector4 operator - (const Vector4& rkVector) const
		{
			return Vector4(x-rkVector.x, y-rkVector.y, z-rkVector.z, w-rkVector.w);
		}

		WGEINLINE Vector4 operator * (const float fscalar) const
		{
			return Vector4(x*fscalar, y*fscalar, z*fscalar, w*fscalar);
		}


		WGEINLINE Vector4 operator / (const float fScalar) const
		{
			assert(fScalar != 0.0);
			float fInv = 1.0/fScalar;

			return Vector4(x*fInv, y*fInv, z*fInv, w*fInv);

		}

		WGEINLINE const Vector4& operator + () const
		{
			return *this;
		}

		WGEINLINE Vector4 operator - () const
		{
			return Vector4(-x, -y, -z, -w);
		}


		///helper function
		WGEINLINE friend Vector4 operator * (const float fScalar, const Vector4& rkVector)
		{
			return Vector4(fScalar* rkVector.x, fScalar*rkVector.y, fScalar*rkVector.z, fScalar*rkVector.w);
		}


		~Vector4(void){}


	public:
		float x, y, z, w;
	};


	class WGEEXPORT Math{
	public:
		/**help function on vectors
		@remarks
		    none
		*/
		///2D Vector
		static WGEINLINE Vector2 wgeVec2Add        (const Vector2& lkVec, const Vector2& rkVec);
		static WGEINLINE Vector2 wgeVec2Subtract   (const Vector2& lkVec, const Vector2& rkVec);
    	static WGEINLINE float   wgeVec2Length     (const Vector2& vec);
		static WGEINLINE float   wgeVec2LengthSq   (const Vector2& vec);
		static WGEINLINE float   wgeVec2DotProduct (const Vector2& lkVec, const Vector2& rkVec);
		static WGEINLINE float   wgeVec2CCW        (const Vector2& lkVec, const Vector2& rkVec);
		static WGEINLINE Vector2 wgeMinimize       (const Vector2& lkVec, const Vector2& rkVec);
		static WGEINLINE Vector2 wgeMaximize       (const Vector2& lkVec, const Vector2& rkVec);
		static WGEINLINE Vector2 wgeVec2Lerp       (const Vector2& lkVec, const Vector2& rkVec, float s); 
		static WGEINLINE Vector2 wgeVec2Normalize  (const Vector2& vec);
		static WGEINLINE Vector2 wgeVec2Hermite    (const Vector2& vec1, const Vector2& T1, const Vector2& vec2, const Vector2 T2, float s);
		static WGEINLINE Vector2 wgeVec2CatmullRom (const Vector2& vec0, const Vector2& vec1, const Vector2& vec2, const Vector2& vec3,  float s);
		static WGEINLINE Vector2 wgeVec2BaryCentric(const Vector2& vec0, const Vector2& vec1, const Vector2& vec2, const Vector2& vec3,  float f,  float g);
		// Transform (x, y, 0, 1) by matrix.
		static WGEINLINE Vector4 wgeVec2Transform  (const Vector2& vec, const Matrix& m);
		// Transform (x, y, 0, 1) by matrix, project result back into w=1.
		static WGEINLINE Vector2 wgeVec2TransformCoord(const Vector2& vec, const Matrix& m);
		// Transform (x, y, 0, 0) by matrix.
		static WGEINLINE Vector2 wgeVec2TransformNormal(const Vector2& vec, const Matrix& m);


		
		
		///3D Vector
		static WGEINLINE Vector3 wgeVec3Add        (const Vector3& lkVec, const Vector3& rkVec);
		static WGEINLINE Vector3 wgeVec3Subtract   (const Vector3& lkVec, const Vector3& rkVec);
		static WGEINLINE float   wgeVec3Length     (const Vector3& vec);
		static WGEINLINE float   wgeVec3LengthSq   (const Vector3& vec);
		static WGEINLINE float   wgeVec3DotProduct (const Vector3& lkVec, const Vector3& rkVec);
		static WGEINLINE Vector3 wgeVec3Cross      (const Vector3& lkVec, const Vector3& rkVec);
		static WGEINLINE Vector3 wgeVec3Minimize   (const Vector3& lkVec, const Vector3& rkVec);
		static WGEINLINE Vector3 wgeVec3Maxmize    (const Vector3& lkVec, const Vector3& rkVec);
		static WGEINLINE Vector3 wgeVec3Lerp       (const Vector3& lkVec, const Vector3& rkVec, float s);
		static WGEINLINE Vector3 wgeVec3Normalize  (const Vector3& vec);
		static WGEINLINE Vector3 wgeVec3Hermite    (const Vector3& vec1, const Vector3& T1, const Vector3& vec2, const Vector3 T2, float s);
		static WGEINLINE Vector3 wgeVec3CatmullRom (const Vector3& vec0, const Vector3& vec1, const Vector3& vec3,  float s);
		static WGEINLINE Vector3 wgeVec3BaryCentric(const Vector3& vec0, const Vector3& vec1, const Vector3& vec3,  float f,  float g);
		// Transform (x, y, z, 1) by matrix.
		static WGEINLINE Vector3 wgeVec3Transform  (const Vector3& vec, const Matrix& m);
		// Transform (x, y, z, 1) by matrix, project result back into w=1.
		static WGEINLINE Vector3 wgeVec3TransformCoord(const Vector3& vec, const Matrix& m);
		// Transform (x, y, z, 0) by matrix.  If you transforming a normal by a 
		// non-affine matrix, the matrix you pass to this function should be the 
		// transpose of the inverse of the matrix you would use to transform a coord.
		static WGEINLINE Vector3 wgeVec3TransformNormal(const Vector3& vec, const Matrix& m);
		
		
		

		///4D Vector
		static WGEINLINE Vector4 wgeVec4Add          (const Vector4& lkVec, const Vector4& rkVec);		
		static WGEINLINE Vector4 wgeVec4Subtract     (const Vector4& lkVec, const Vector4& rkVec);
		static WGEINLINE float   wgeVec4DotProduct   (const Vector4& lkVec, const Vector4& rkVec);
		static WGEINLINE float   wgeVec4Length       (const Vector4& vec);
		static WGEINLINE float   wgeVec4LengthSq     (const Vector4& vec);
		static WGEINLINE Vector4 wgeVec4Minimize     (const Vector4& lkVec, const Vector4& rkVec);
		static WGEINLINE Vector4 wgeVec4Maxmize      (const Vector4& lkVec, const Vector4& rkVec);
	    static WGEINLINE Vector4 wgeVec4Lerp         (const Vector4& lkVec, const Vector4& rkVec, float s);
        static WGEINLINE Vector4 wgeVec4Cross        (const Vector4& vec1,  const Vector4& vec2, const Vector4 vec3);
		static WGEINLINE Vector4 wgeVec4Normalize  (const Vector4& vec);
		static WGEINLINE Vector4 wgeVec4Hermite    (const Vector4& vec1, const Vector4& T1, const Vector4& vec2, const Vector4 T2, float s);
		static WGEINLINE Vector4 wgeVec4CatmullRom (const Vector4& vec0, const Vector4& vec1, const Vector4& vec4,  float s);
		static WGEINLINE Vector4 wgeVec4BaryCentric(const Vector4& vec0, const Vector4& vec1, const Vector4& vec4,  float f,  float g);
		// Transform (x, y, z, 1) by matrix.
		static WGEINLINE Vector4 wgeVec4Transform  (const Vector4& vec, const Matrix& m);

	};
}
#endif
